package com.hopes.bizz.poll.statistic.service.impl;

import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.hopes.bizz.common.core.util.CollectionUtils;
import com.hopes.bizz.common.core.util.FormatNumUtils;
import com.hopes.bizz.common.core.util.NumberUtil;
import com.hopes.bizz.common.core.util.StringUtils;
import com.hopes.bizz.poll.analy.dto.ReportDTO;
import com.hopes.bizz.poll.analy.vo.ReportVO;
import com.hopes.bizz.poll.common.constant.OmConstant;
import com.hopes.bizz.poll.common.constant.ReportConstant;
import com.hopes.bizz.poll.monitor.entity.PollMonitorFactorEntity;
import com.hopes.bizz.poll.monitor.entity.PollMonitorTypeEntity;
import com.hopes.bizz.poll.monitor.service.PollMonitorFactorService;
import com.hopes.bizz.poll.monitor.service.PollMonitorTypeService;
import com.hopes.bizz.poll.om.entity.BaseOmDataEntity;
import com.hopes.bizz.poll.om.entity.PollDayEntity;
import com.hopes.bizz.poll.om.entity.PollHourEntity;
import com.hopes.bizz.poll.om.entity.PollMonthEntity;
import com.hopes.bizz.poll.om.service.PollDayService;
import com.hopes.bizz.poll.om.service.PollHourService;
import com.hopes.bizz.poll.om.service.PollMonthService;
import com.hopes.bizz.poll.statistic.service.DataReportService;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @author yumr
 * @ProjectName bizz
 * @Description 数据报表
 * @time 2023/6/6 9:48
 */
@Service
@RequiredArgsConstructor
public class DataReportServiceImpl implements DataReportService {

    private final PollHourService pollHourService;
    private final PollDayService pollDayService;
    private final PollMonthService pollMonthService;
    private final PollMonitorFactorService pollMonitorFactorService;
    private final PollMonitorTypeService pollMonitorTypeService;


    @Override
    public List<ReportVO> reportAnalyze(ReportDTO reportDTO) {
        if (reportDTO == null || reportDTO.getDateType() == null) {
            return Collections.emptyList();
        }
        this.createSearchTime(reportDTO);
        switch (reportDTO.getDateType()) {
            case ReportConstant.DATA_TYPE_DAY: {
                return dayReportAnalyze(reportDTO.getMonitorSiteId(), reportDTO.getBeginTime(), reportDTO.getEndTime(), Calendar.HOUR_OF_DAY);
            }
            case ReportConstant.DATA_TYPE_MONTH: {
                return monthReportAnalyze(reportDTO.getMonitorSiteId(), reportDTO.getBeginTime(), reportDTO.getEndTime(), Calendar.DAY_OF_MONTH);
            }
            case ReportConstant.DATA_TYPE_YEAR:
            case ReportConstant.DATA_TYPE_SEASON: {
                return yearReportAnalyze(reportDTO.getMonitorSiteId(), reportDTO.getBeginTime(), reportDTO.getEndTime(), Calendar.MONTH);
            }
        }
        return Collections.emptyList();
    }


    /**
     * 查询日报表数据
     * @param siteId      站点Id
     * @param beginTime   开始时间
     * @param endTime     结束时间
     * @param calendar    获取时间的栏位
     * @return            结果
     */
    public List<ReportVO> dayReportAnalyze(Long siteId, Date beginTime, Date endTime, int calendar) {
        final LambdaQueryWrapper<PollHourEntity> queryWrapper = Wrappers.<PollHourEntity>lambdaQuery().select(
                PollHourEntity::getDataTime, PollHourEntity::getDataJson)
                .eq(PollHourEntity::getMonitorSiteId, siteId)
                .ge(PollHourEntity::getDataTime, beginTime).le(PollHourEntity::getDataTime, endTime).orderByAsc(PollHourEntity::getDataTime);
        final List<PollHourEntity> list = pollHourService.list(queryWrapper);
        if (list.isEmpty()) {
            return Collections.emptyList();
        }
        // 监测数据处理
        return handlerFillReportList(siteId, calendar, list);
    }

    public List<ReportVO> monthReportAnalyze(Long siteId, Date beginTime, Date endTime, int calendar) {
        final LambdaQueryWrapper<PollDayEntity> queryWrapper = Wrappers.<PollDayEntity>lambdaQuery().select(
                PollDayEntity::getDataTime, PollDayEntity::getDataJson)
                .eq(PollDayEntity::getMonitorSiteId, siteId)
                .ge(PollDayEntity::getDataTime, beginTime).le(PollDayEntity::getDataTime, endTime);
        final List<PollDayEntity> list = pollDayService.list(queryWrapper);
        if (list.isEmpty()) {
            return Collections.emptyList();
        }
        return handlerFillReportList(siteId, calendar, list);
    }

    public List<ReportVO> yearReportAnalyze(Long siteId, Date beginTime, Date endTime, int calendar) {
        final LambdaQueryWrapper<PollMonthEntity> queryWrapper = Wrappers.<PollMonthEntity>lambdaQuery().select(
                PollMonthEntity::getDataTime, PollMonthEntity::getDataJson)
                .eq(PollMonthEntity::getMonitorSiteId, siteId)
                .ge(PollMonthEntity::getDataTime, beginTime).le(PollMonthEntity::getDataTime, endTime);
        final List<PollMonthEntity> list = pollMonthService.list(queryWrapper);
        if (list.isEmpty()) {
            return Collections.emptyList();
        }
        return handlerFillReportList(siteId, calendar, list);
    }

    @NotNull
    private List<ReportVO> handlerFillReportList(Long siteId, int calendar, List<? extends BaseOmDataEntity> list) {
        final List<ReportVO> reportList = handlerBaseOmDataList(calendar, list);

        // 扩展数据 平均值、最大值、最小值、样本数、排放量
        final LambdaQueryWrapper<PollMonitorFactorEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(PollMonitorFactorEntity::getMonitorSiteId, siteId);
        final List<PollMonitorFactorEntity> monitorFactorList = pollMonitorFactorService.list(wrapper);

        PollMonitorTypeEntity monitorType = pollMonitorTypeService.getBySiteId(siteId);
        Boolean zsFlag = monitorType.getZsFlag();

        final ReportVO reportAvg = buildExtendDataByList(ReportConstant.DATA_TITLE_AVG, reportList.size() + 1,  monitorFactorList, reportList, zsFlag);
        final ReportVO reportMax = buildExtendDataByList(ReportConstant.DATA_TITLE_MAX, reportList.size() + 2,  monitorFactorList, reportList, zsFlag);
        final ReportVO reportMin = buildExtendDataByList(ReportConstant.DATA_TITLE_MIN, reportList.size() + 3,  monitorFactorList, reportList, zsFlag);
        final ReportVO reportSample = buildExtendDataByList(ReportConstant.DATA_TITLE_SAMPLE, reportList.size() + 4,  monitorFactorList, reportList, zsFlag);
        final ReportVO reportCus = buildExtendDataByList(ReportConstant.DATA_TITLE_COU, reportList.size() + 5,  monitorFactorList, reportList, zsFlag);

        // 添加扩展数据
        reportList.addAll(Arrays.asList(reportAvg, reportMax, reportMin, reportSample, reportCus));
        return reportList;
    }

    private ReportVO buildExtendDataByList(String title, int key, List<PollMonitorFactorEntity> monitorFactorList, List<ReportVO> reportList, Boolean zsFlag) {
        final ReportVO report = new ReportVO();
        report.setKey(key);
        report.setTitle(title);

        switch (title) {
            case ReportConstant.DATA_TITLE_AVG:
                report.setData(buildAvgDataJson(monitorFactorList, reportList, zsFlag));
                break;
            case ReportConstant.DATA_TITLE_MAX:
                report.setData(buildMaxDataJson(monitorFactorList, reportList, zsFlag));
                break;
            case ReportConstant.DATA_TITLE_MIN:
                report.setData(buildMinDataJson(monitorFactorList, reportList, zsFlag));
                break;
            case ReportConstant.DATA_TITLE_SAMPLE:
                report.setData(buildSampleDataJson(monitorFactorList, reportList, zsFlag));
                break;
            case ReportConstant.DATA_TITLE_COU:
                report.setData(buildCusDataJson(monitorFactorList, reportList));
                break;
        }
        return report;
    }

    private JSONObject buildCusDataJson(List<PollMonitorFactorEntity> monitorFactorList, List<ReportVO> reportList) {
        final JSONObject result = new JSONObject();
        for (PollMonitorFactorEntity monitorFactor : monitorFactorList) {
            final JSONObject data = new JSONObject();
            // 某个因子的JSON数据集合
            final List<JSONObject> collect = reportList.stream().map(ReportVO::getData).map(item -> item.getJSONObject(monitorFactor.getFactorCode())).filter(MapUtil::isNotEmpty).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(collect)) {
                // 计算排放量
                final List<BigDecimal> cusList = collect.stream().map(obj -> obj.getBigDecimal(OmConstant.COU)).filter(Objects::nonNull).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(cusList)) {
                    data.put(OmConstant.COU, NumberUtil.toScaledDouble(cusList.stream().mapToDouble(BigDecimal::doubleValue).sum(), 3));
                }
            }
            result.put(monitorFactor.getFactorCode(), data);
        }
        return result;
    }

    private JSONObject buildSampleDataJson(List<PollMonitorFactorEntity> monitorFactorList, List<ReportVO> reportList, Boolean zsFlag) {
        final JSONObject result = new JSONObject();
        for (PollMonitorFactorEntity monitorFactor : monitorFactorList) {
            final JSONObject data = new JSONObject();
            // 某个因子的JSON数据集合
            final List<JSONObject> collect = reportList.stream().map(ReportVO::getData).map(item -> item.getJSONObject(monitorFactor.getFactorCode())).filter(MapUtil::isNotEmpty).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(collect)) {
                // 实时数据样本数
//                final List<BigDecimal> valList = collect.stream().map(obj -> obj.getBigDecimal(OmConstant.VAL)).filter(Objects::nonNull).collect(Collectors.toList());
//                if (CollectionUtils.isNotEmpty(valList)) {
//                    data.put(OmConstant.VAL, valList.size());
//                }
                // 计算均值样本数
                final List<BigDecimal> avgList = collect.stream().map(obj -> obj.getBigDecimal(OmConstant.VAL)).filter(Objects::nonNull).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(avgList)) {
                    data.put(OmConstant.VAL, avgList.size());
                }
                // 计算折算样本数
                final List<BigDecimal> zsValList = collect.stream().map(obj -> obj.getBigDecimal(OmConstant.ZS_VAL)).filter(Objects::nonNull).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(zsValList)) {
                    data.put(OmConstant.ZS_VAL, zsValList.size());
                }
                // 计算排放量样本数
                final List<BigDecimal> cusList = collect.stream().map(obj -> obj.getBigDecimal(OmConstant.COU)).filter(Objects::nonNull).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(cusList)) {
                    data.put(OmConstant.COU, cusList.size());
                }
            }
            result.put(monitorFactor.getFactorCode(), data);
        }
        return result;
    }

    private JSONObject buildMinDataJson(List<PollMonitorFactorEntity> monitorFactorList, List<ReportVO> reportList, Boolean zsFlag) {
        final JSONObject result = new JSONObject();
        for (PollMonitorFactorEntity monitorFactor : monitorFactorList) {
            final JSONObject data = new JSONObject();
            // 某个因子的JSON数据集合
            final List<JSONObject> collect = reportList.stream().map(ReportVO::getData).map(item -> item.getJSONObject(monitorFactor.getFactorCode())).filter(MapUtil::isNotEmpty).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(collect)) {
                // 实时数据
//                final List<BigDecimal> valList = collect.stream().map(obj -> obj.getBigDecimal("val")).filter(Objects::nonNull).collect(Collectors.toList());
//                if (CollectionUtils.isNotEmpty(valList)) {
//                    data.put("val", valList.stream().min(BigDecimal::compareTo).orElse(BigDecimal.ZERO));
//                }
                final List<BigDecimal> avgList = collect.stream().map(obj -> obj.getBigDecimal(OmConstant.VAL)).filter(Objects::nonNull).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(avgList)) {
                    // 计算均值的最小值
                    data.put(OmConstant.VAL, avgList.stream().filter(Objects::nonNull).min(BigDecimal::compareTo).orElse(BigDecimal.ZERO));
                }
                if (zsFlag) {
                    // 计算折算最小值
                    data.put(OmConstant.ZS_VAL, collect.stream().map(obj -> obj.getBigDecimal(OmConstant.ZS_VAL)).filter(Objects::nonNull).min(BigDecimal::compareTo).orElse(BigDecimal.ZERO));
                }
                if (StringUtils.isNotBlank(monitorFactor.getStatisticUnit())) {
                    // 计算排放量最小值
                    data.put(OmConstant.COU, collect.stream().map(obj -> obj.getBigDecimal(OmConstant.COU)).filter(Objects::nonNull).min(BigDecimal::compareTo).orElse(BigDecimal.ZERO));
                }
            }
            result.put(monitorFactor.getFactorCode(), data);
        }
        return result;
    }

    private JSONObject buildMaxDataJson(List<PollMonitorFactorEntity> monitorFactorList, List<ReportVO> reportList, Boolean zsFlag) {
        final JSONObject result = new JSONObject();
        for (PollMonitorFactorEntity monitorFactor : monitorFactorList) {
            final JSONObject data = new JSONObject();
            // 某个因子的JSON数据集合
            final List<JSONObject> collect = reportList.stream().map(ReportVO::getData).map(item -> item.getJSONObject(monitorFactor.getFactorCode())).filter(MapUtil::isNotEmpty).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(collect)) {
                // 实时数据
//                final List<BigDecimal> valList = collect.stream().map(obj -> obj.getBigDecimal("val")).filter(Objects::nonNull).collect(Collectors.toList());
//                if (CollectionUtils.isNotEmpty(valList)) {
//                    data.put("val", valList.stream().max(BigDecimal::compareTo).orElse(BigDecimal.ZERO));
//                }
                final List<BigDecimal> avgList = collect.stream().map(obj -> obj.getBigDecimal(OmConstant.VAL)).filter(Objects::nonNull).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(avgList)) {
                    data.put(OmConstant.VAL, avgList.stream().filter(Objects::nonNull).max(BigDecimal::compareTo).orElse(BigDecimal.ZERO));
                }
                if (zsFlag) {
                    data.put(OmConstant.ZS_VAL, collect.stream().map(obj -> obj.getBigDecimal(OmConstant.ZS_VAL)).filter(Objects::nonNull).max(BigDecimal::compareTo).orElse(BigDecimal.ZERO));
                }
                if (StringUtils.isNotBlank(monitorFactor.getStatisticUnit())) {
                    data.put(OmConstant.COU, collect.stream().map(obj -> obj.getBigDecimal(OmConstant.COU)).filter(Objects::nonNull).max(BigDecimal::compareTo).orElse(BigDecimal.ZERO));
                }
            }
            result.put(monitorFactor.getFactorCode(), data);
        }
        return result;
    }

    /**
     * 构建监测因子 平均值 JSON 数据
     * @param monitorFactorList  监测因子
     * @param reportList         监测数据
     * @return                   返回值
     */
    private JSONObject buildAvgDataJson(List<PollMonitorFactorEntity> monitorFactorList, List<ReportVO> reportList, Boolean zsFlag) {
        final JSONObject result = new JSONObject();
        for (PollMonitorFactorEntity monitorFactor : monitorFactorList) {
            final JSONObject data = new JSONObject();
            // 某个因子的JSON数据集合
            final List<JSONObject> collect = reportList.stream().map(ReportVO::getData).map(item -> item.getJSONObject(monitorFactor.getFactorCode())).filter(MapUtil::isNotEmpty).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(collect)) {
//                // 实时数据
//                final List<BigDecimal> valList = collect.stream().map(obj -> obj.getBigDecimal("val")).filter(Objects::nonNull).collect(Collectors.toList());
//                if (CollectionUtils.isNotEmpty(valList)) {
//                    data.put("val", NumberUtil.toScaledDouble(valList.stream().mapToDouble(BigDecimal::doubleValue).average().orElse(0), 3));
//                }
                // 平均值
                final List<BigDecimal> avgList = collect.stream().map(obj -> obj.getBigDecimal(OmConstant.VAL)).filter(Objects::nonNull).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(avgList)) {
                    // 计算均值
                    data.put(OmConstant.VAL, NumberUtil.toScaledDouble(avgList.stream().mapToDouble(BigDecimal::doubleValue).average().orElse(0), 3));
                }
                if (zsFlag) {
                    // 计算折算均值
                    final double zsAvg = collect.stream().map(obj -> obj.getBigDecimal(OmConstant.ZS_VAL)).filter(Objects::nonNull).mapToDouble(BigDecimal::doubleValue).average().orElse(0);
                    data.put(OmConstant.ZS_VAL, NumberUtil.toScaledDouble(zsAvg, 3));
                }
                if (StringUtils.isNotBlank(monitorFactor.getStatisticUnit())) {
                    // 计算排放量
                    final double cus = collect.stream().map(obj -> obj.getBigDecimal(OmConstant.COU)).filter(Objects::nonNull).mapToDouble(BigDecimal::doubleValue).average().orElse(0);
                    data.put(OmConstant.COU, NumberUtil.toScaledDouble(cus, 3));
                }
            }
            result.put(monitorFactor.getFactorCode(), data);
        }
        return result;
    }


    /**
     * 处理监测数据
     * @param calendar  时间单位
     * @param list      监测数据
     * @return          结果
     */
    private List<ReportVO> handlerBaseOmDataList(int calendar, List<? extends BaseOmDataEntity> list) {
        final List<ReportVO> result = new ArrayList<>();
        if (CollectionUtils.isEmpty(list)) {
            return result;
        }
        final Calendar instance = Calendar.getInstance();
        final AtomicInteger key = new AtomicInteger(0);
        for (BaseOmDataEntity omDataEntity : list) {
            final ReportVO reportVO = new ReportVO();
            reportVO.setKey(key.incrementAndGet());

            instance.setTime(omDataEntity.getDataTime());
            final String prefix = FormatNumUtils.frontCompWithZero(2, instance.get(calendar));
            final String suffix = FormatNumUtils.frontCompWithZero(2, instance.get(calendar) + 1);
            if (calendar == Calendar.HOUR_OF_DAY) {
                reportVO.setTitle(prefix + "-" + suffix + "点");
            } else if (calendar == Calendar.DAY_OF_MONTH) {
                reportVO.setTitle(prefix + "-" + suffix + "号");
            } else if (calendar == Calendar.MONTH) {
                reportVO.setTitle(prefix + "-" + suffix + "月");
            } else  {
                reportVO.setTitle(prefix + "-" + suffix);
            }

            reportVO.setData(omDataEntity.getDataJson());

            result.add(reportVO);
        }
        return result;
    }

    private void createSearchTime(ReportDTO reportDTO){
        Calendar cal = Calendar.getInstance();
        if (reportDTO.getReportTime() != null) {
            cal.setTime(reportDTO.getReportTime());
        }
        switch (reportDTO.getDateType()) {
            case ReportConstant.DATA_TYPE_DAY:
                reportDTO.setBeginTime(cal.getTime());
                cal.add(Calendar.DAY_OF_YEAR, 1);
                cal.add(Calendar.SECOND, -1);
                reportDTO.setEndTime(cal.getTime());
                break;
            case ReportConstant.DATA_TYPE_MONTH:
                cal.set(Calendar.DAY_OF_MONTH, 1);
                reportDTO.setBeginTime(cal.getTime());
                cal.add(Calendar.MONTH, 1);
                cal.add(Calendar.SECOND, -1);
                reportDTO.setEndTime(cal.getTime());
                break;
            case ReportConstant.DATA_TYPE_YEAR:
                cal.set(Calendar.DAY_OF_YEAR, 1);
                cal.set(Calendar.MONTH, 1);
                reportDTO.setBeginTime(cal.getTime());
                cal.add(Calendar.YEAR, 1);
                cal.add(Calendar.SECOND, -1);
                reportDTO.setEndTime(cal.getTime());
                break;
            case ReportConstant.DATA_TYPE_SEASON:  // 季度
                seasonSearchTime(cal, reportDTO, cal.get(Calendar.MONTH) + 1);
                break;
        }
    }

    private void seasonSearchTime(Calendar cal, ReportDTO reportDTO, int flag){
        switch (flag){
            case 0: // 第一季度 1-3
                cal.set(Calendar.MONTH,0);
                reportDTO.setBeginTime(cal.getTime());
                cal.set(Calendar.MONTH,2);
                reportDTO.setEndTime(cal.getTime());
                break;
            case 1:// 第二季度 4-6
                cal.set(Calendar.MONTH,3);
                reportDTO.setBeginTime(cal.getTime());
                cal.set(Calendar.MONTH,5);
                reportDTO.setEndTime(cal.getTime());
                break;
            case 2:// 第三季度 6-9
                cal.set(Calendar.MONTH,6);
                reportDTO.setBeginTime(cal.getTime());
                cal.set(Calendar.MONTH,8);
                reportDTO.setEndTime(cal.getTime());
                break;
            case 3:// 第四季度 10-12
                cal.set(Calendar.MONTH,9);
                reportDTO.setBeginTime(cal.getTime());
                cal.set(Calendar.MONTH,11);
                reportDTO.setEndTime(cal.getTime());
                break;
        }
    }
}
